<template>
  <div class="canvas-table-container" ref="container">
    <canvas ref="canvas"></canvas>
  </div>
</template>

<script>
export default {
  name: "CanvasTable",
  props: {
    columns: {
      type: Array,
      default: () => [],
    },
    data: {
      type: Array,
      default: () => [],
    },
    rowHeight: {
      type: Number,
      default: 40,
    },
    headerHeight: {
      type: Number,
      default: 50,
    },
  },
  data() {
    return {
      canvas: null,
      ctx: null,
      container: null,
      scrollTop: 0,
      visibleStartIndex: 0,
      visibleEndIndex: 0,
      dpr: window.devicePixelRatio || 1,
    };
  },
  computed: {
    totalHeight() {
      return this.data.length * this.rowHeight + this.headerHeight;
    },
    visibleCount() {
      const contentHeight =
        this.container?.clientHeight - this.headerHeight || 0;
      return Math.ceil(contentHeight / this.rowHeight) + 5; // 增加5行缓冲
    },
  },
  watch: {
    data() {
      this.updateContainerScroll();
      this.draw();
    },
  },
  mounted() {
    this.initCanvas();
    this.setupEvents();
    this.updateContainerScroll();
    this.draw();
  },
  methods: {
    initCanvas() {
      this.container = this.$refs.container;
      this.canvas = this.$refs.canvas;
      this.ctx = this.canvas.getContext("2d");

      const rect = this.container.getBoundingClientRect();
      this.canvas.width = rect.width * this.dpr;
      this.canvas.height = rect.height * this.dpr;
      this.ctx.scale(this.dpr, this.dpr);

      Object.assign(this.container.style, {
        height: "100vh",
        overflow: "auto",
        position: "relative",
      });

      Object.assign(this.canvas.style, {
        position: "absolute",
        top: "0",
        left: "0",
      });
    },

    setupEvents() {
      this.container.addEventListener("scroll", this.handleScroll);
      window.addEventListener("resize", this.handleResize);
    },

    handleScroll(e) {
      requestAnimationFrame(() => {
        this.scrollTop = e.target.scrollTop;
        this.calculateVisibleRange();
        this.draw();
      });
    },

    handleResize() {
      this.initCanvas();
      this.draw();
    },

    calculateVisibleRange() {
      const scrollOffset = Math.max(0, this.scrollTop - this.headerHeight);
      this.visibleStartIndex = Math.max(
        0,
        Math.floor(scrollOffset / this.rowHeight) - 2 // 提前2行渲染
      );
      this.visibleEndIndex = Math.min(
        this.visibleStartIndex + this.visibleCount,
        this.data.length
      );
    },

    updateContainerScroll() {
      this.container.scrollTop = 0;
      this.scrollTop = 0;
    },

    draw() {
      this.clearCanvas();
      this.drawHeader();
      this.drawRows();
    },

    clearCanvas() {
      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    },

    drawHeader() {
      this.ctx.save();
      this.ctx.fillStyle = "#f8f9fa";
      this.ctx.fillRect(0, 0, this.canvas.width, this.headerHeight);

      let xPos = 0;
      this.columns.forEach((col, index) => {
        this.ctx.fillStyle = "#495057";
        this.ctx.font = "14px system-ui";
        this.ctx.textBaseline = "middle";
        this.ctx.fillText(col.title, xPos + 12, this.headerHeight / 2);
        xPos += col.width;
      });

      this.ctx.restore();
    },

    drawRows() {
      if (!this.data.length) return;

      const startY = this.headerHeight;
      const scrollOffset = Math.max(0, this.scrollTop - this.headerHeight);
      const visibleData = this.data.slice(
        this.visibleStartIndex,
        this.visibleEndIndex
      );

      visibleData.forEach((row, rowIndex) => {
        const actualIndex = this.visibleStartIndex + rowIndex;
        const yPos =
          startY +
          actualIndex * this.rowHeight -
          (scrollOffset % this.rowHeight);

        // 背景色
        this.ctx.fillStyle = actualIndex % 2 === 0 ? "#fff" : "#f8f9fa";
        this.ctx.fillRect(0, yPos, this.canvas.width, this.rowHeight);

        // 单元格内容
        let xPos = 0;
        this.columns.forEach((col) => {
          this.ctx.fillStyle = "#212529";
          this.ctx.font = "14px system-ui";
          this.ctx.textBaseline = "middle";
          this.ctx.fillText(
            row[col.key] || "-",
            xPos + 12,
            yPos + this.rowHeight / 2
          );
          xPos += col.width;
        });

        // 底部边框
        this.ctx.strokeStyle = "#dee2e6";
        this.ctx.beginPath();
        this.ctx.moveTo(0, yPos + this.rowHeight - 0.5);
        this.ctx.lineTo(this.canvas.width, yPos + this.rowHeight - 0.5);
        this.ctx.stroke();
      });
    },
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.handleResize);
  },
};
</script>

<style scoped>
.canvas-table-container {
  width: 100%;
  height: 100vh;
  -webkit-overflow-scrolling: touch;
  transform: translateZ(0);
}
</style>
